using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Quartz;
using FytSoa.Common.Utils;
using FytSoa.Domain.Core.Result;
using Quartz.Impl.Matchers;
using System.Linq;
using Quartz.Impl.Triggers;
using Quartz.Impl;

namespace FytSoa.Quartz
{
    /// <summary>
    /// 任务调度服务
    /// </summary>
    public class QuartzService:IQuartzService
    {
        /// <summary>
        /// 任务调度对象
        /// </summary>
        public static readonly QuartzService Instance;

        static QuartzService()
        {
            Instance = new QuartzService();
        }
        
        private static IScheduler _scheduler;
        
        /// <summary>
        /// 返回任务计划（调度器）
        /// </summary>
        /// <returns></returns>
        private IScheduler Scheduler
        {
            get
            {
                if (_scheduler != null)
                {
                    return _scheduler;
                }
                ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
                _scheduler = schedulerFactory.GetScheduler().Result;
                _scheduler.Start();//默认开始调度器
                return _scheduler;
            }
        }
        
        /// <summary>
        /// 添加任务
        /// </summary>
        /// <param name="type">类</param>
        /// <param name="jobKey">键</param>
        /// <param name="trigger">触发器</param>
        public async Task<ApiResult<string>> Add(Type type, JobKey jobKey, ITrigger trigger = null)
        {
            //检查任务是否已存在
            if (await Scheduler.CheckExists(jobKey))
            {
                return JResult<string>.Error("任务已存在");
            }
            trigger ??= TriggerBuilder.Create()
                .WithIdentity(jobKey.Name)
                .WithDescription(jobKey.Group+"_"+jobKey.Name)
                .WithSimpleSchedule(x => x.WithMisfireHandlingInstructionFireNow().WithRepeatCount(-1))
                .Build();
            var job = JobBuilder.Create(type)
                .WithIdentity(jobKey)
                .Build();
            await Scheduler.ScheduleJob(job, trigger);
            return JResult<string>.Success();
        }

        /// <summary>
        /// 添加一个http请求的任务
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public async Task<ApiResult<string>> Add(SchedulerEntity entity)
        {
            //检查任务是否已存在
            var jobKey = new JobKey(entity.JobName, entity.JobGroup);
            if (await Scheduler.CheckExists(jobKey))
            {
                return JResult<string>.Error("任务已存在");
            }
            //http请求配置
            var httpDir = new Dictionary<string, string>()
            {
                { "RequestUrl",entity.RequestUrl},
                { "RequestParameters",entity.RequestParameters},
                { "RequestType", ((int)entity.RequestType).ToString()},
                { Constant.HEADERS, entity.Headers},
                { Constant.MAILMESSAGE, ((int)entity.MailMessage).ToString()},
            };
            // 定义这个工作，并将其绑定到我们的IJob实现类                
            var job = JobBuilder.Create<HttpJob>()
                .SetJobData(new JobDataMap(httpDir))
                .WithDescription(entity.Description)
                .WithIdentity(entity.JobName, entity.JobGroup)
                .Build();
            
            var trigger = TriggerBuilder.Create()
                .WithIdentity(entity.JobName, entity.JobGroup)
                .StartAt(entity.BeginTime)//开始时间
                .EndAt(entity.EndTime)//结束时间
                .WithCronSchedule(entity.Cron, cronScheduleBuilder => cronScheduleBuilder.WithMisfireHandlingInstructionFireAndProceed())//指定cron表达式
                .ForJob(entity.JobName, entity.JobGroup)//作业名称
                .Build();
            
            // Quartz使用我们的触发器来安排作业
            await Scheduler.ScheduleJob(job, trigger);
            return JResult<string>.Success();
        }

        /// <summary>
        /// 初始化开启任务调度器
        /// </summary>
        /// <returns></returns>
        public async Task<bool> InitStart() 
        {
            if (Scheduler.InStandbyMode)
            {
                var groups = await Scheduler.GetJobGroupNames();
                foreach (var groupName in groups)
                {
                    foreach (var jobKey in await Scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)))
                    {
                        var triggers = await Scheduler.GetTriggersOfJob(jobKey);
                        var trigger = triggers.AsEnumerable().FirstOrDefault();
                        var state = await Scheduler.GetTriggerState(trigger.Key);
                        if (state == TriggerState.Paused)
                            await Scheduler.ResumeTrigger(trigger.Key);
                    }
                }
                await Scheduler.Start();
                Logger.Info("Quartz——>>>>>>开启调度器");
            }
            return Scheduler.InStandbyMode;
        }
        
        /// <summary>
        /// 根据任务查询运行日志
        /// </summary>
        /// <returns></returns>
        public async Task<List<string>> JobLogs(JobKey jobKey)
        {
            var jobDetail = await Scheduler.GetJobDetail(jobKey);
            return jobDetail?.JobDataMap["LogList"] as List<string>;
        }

        /// <summary>
        /// 查询所有任务
        /// </summary>
        /// <returns></returns>
        public async Task<List<SchedulerEntity>> GetJobListAsync()
        {
            var list = new List<SchedulerEntity>();
            var groupNames = await Scheduler.GetJobGroupNames();
            foreach (var groupName in groupNames.OrderBy(t => t))
            {
                foreach (var jobKey in await Scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)))
                {
                    var triggers = await Scheduler.GetTriggersOfJob(jobKey);
                    var trigger = triggers.AsEnumerable().FirstOrDefault();
                    var jobDetail = await Scheduler.GetJobDetail(jobKey);
                    var createTimeStr = jobDetail.JobDataMap.GetString(Constant.CREATETIME);
                    list.Add(new SchedulerEntity() {
                        JobName=jobKey.Name,
                        JobGroup=jobKey.Group,
                        Cron= (trigger as CronTriggerImpl)?.CronExpressionString,
                        TriggerState= await Scheduler.GetTriggerState(trigger.Key),
                        PreviousFireTime= trigger.GetPreviousFireTimeUtc()?.LocalDateTime,
                        NextFireTime= trigger.GetNextFireTimeUtc()?.LocalDateTime,
                        Description= jobDetail.Description,
                        RequestUrl = jobDetail.JobDataMap.GetString(Constant.REQUESTURL),
                        RequestType = (RequestTypeEnum)int.Parse(jobDetail.JobDataMap.GetString(Constant.REQUESTTYPE)),
                        Headers = jobDetail.JobDataMap.GetString(Constant.HEADERS),
                        RequestParameters = jobDetail.JobDataMap.GetString(Constant.REQUESTPARAMETERS),
                        CreateTime = string.IsNullOrEmpty(createTimeStr) ? DateTime.Now : DateTime.Parse(jobDetail.JobDataMap.GetString(Constant.CREATETIME))
                    });
                }
            }
            return list.OrderByDescending(m=>m.CreateTime).ToList();
        }

        /// <summary>
        /// 触发执行任务
        /// </summary>
        /// <param name="jobKey"></param>
        /// <param name="action"></param>
        /// <returns></returns>

        public async Task TriggerAction(JobKey jobKey , JobActionEnum action) {
            if (string.IsNullOrEmpty(jobKey.Name) || string.IsNullOrEmpty(jobKey.Group))
            {
                throw new Exception($"任务名或组名不能为空");
            }
            if (!await Scheduler.CheckExists(jobKey))
            {
                throw new Exception($"任务不存在");
            }
            var triggerKey = new TriggerKey(jobKey.Name, jobKey.Group);
            switch (action)
            {
                case JobActionEnum.删除:
                    await Scheduler.PauseJob(jobKey);
                    await Scheduler.DeleteJob(jobKey);
                    Logger.Info("Quartz——>>>>>>删除任务："+jobKey.Name);
                    break;
                case JobActionEnum.修改:
                    await Scheduler.PauseJob(jobKey);
                    await Scheduler.UnscheduleJob(triggerKey);
                    await Scheduler.DeleteJob(jobKey);
                    Logger.Info("Quartz——>>>>>>修改任务：" + jobKey.Name);
                    break;
                case JobActionEnum.暂停:
                    //await Scheduler.PauseTrigger(triggerKey);
                    await Scheduler.PauseJob(jobKey);
                    Logger.Info("Quartz——>>>>>>暂定任务：" + jobKey.Name);
                    break;
                case JobActionEnum.停止:
                    await Scheduler.Shutdown();
                    Logger.Info("Quartz——>>>>>>停止任务");
                    break;
                case JobActionEnum.开启:
                    await Scheduler.Start();
                    Logger.Info("Quartz——>>>>>>开启任务");
                    break;
                case JobActionEnum.恢复:
                    //await Scheduler.ResumeTrigger(triggerKey);
                    await Scheduler.ResumeJob(jobKey);
                    Logger.Info("Quartz——>>>>>>恢复任务：" + jobKey.Name);
                    break;
                case JobActionEnum.立即执行:
                    await Scheduler.TriggerJob(jobKey);
                    Logger.Info("Quartz——>>>>>>立即执行一次任务：" + jobKey.Name);
                    break;
                default:
                    break;
            }
        }
    }
}